﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Autofac;
using Moq;
using Ue.CQRS;
using Ue.CQRS.Bus;
using Ue.CQRS.Events;
using Ue.CQRS.Results;
using Ue.CQRS.DistributionManager;

namespace Ue.CQRS.Tests.Attribute
{
    [TestClass]
    [EventSource]
    public class EventAttributeTests
    {
        public IList<IEvent> sentEvents;

        [TestInitialize]
        public void Initial()
        {
            this.sentEvents = new List<IEvent>();

            var mockBus = new Mock<IBus>();
            mockBus.Setup(bus => bus.Send(It.IsAny<IEvent>()))
                .Callback<IEvent>(domainEvent => sentEvents.Add(domainEvent));

            var containerBuilder = new ContainerBuilder();
            containerBuilder.RegisterInstance<IBus>(mockBus.Object).SingleInstance();
            containerBuilder.RegisterInstance<IDistributor>(new Mock<IDistributor>().Object).SingleInstance();

            var appContext = new ApplicationContext(containerBuilder);
           
            appContext.Start();
        }

        #region OnExit

        [TestMethod]
        public void EventSenderSourceIsNullShouldThrowException()
        {
            var eventAttrbiute = new EventAttribute();

            try
            {
                var method=this.GetMethod("EventSenderSourceIsNullShouldThrowException");

                eventAttrbiute.OnExit(null, method, Builder.Event("event1"));

                Assert.Fail("事件源对象source为null时，应该抛出ArgumentNullException异常");
            }
            catch (ArgumentNullException)
            {

            }
        }

        [TestMethod]
        public void DomainMessageIsNullShouldThrowException()
        {
            var eventAttrbiute = new EventAttribute();

            try
            {
                var method = this.GetMethod("EventSenderSourceIsNullShouldThrowException");

                eventAttrbiute.OnExit(this, method, null);

                Assert.Fail("当领域消息为null时，应该抛出ArgumentNullException异常");
            }
            catch (ArgumentNullException)
            {
                
            }
        }

        [TestMethod]
        public void SendDomainEvent()
        {
            var eventAttrbiute = new EventAttribute()
            {
                Name = "TestEvent",
            };
            var method = this.GetMethod("SendDomainEvent");

            eventAttrbiute.OnExit(this, method, "eventArgs");

            Assert.AreEqual(1, this.sentEvents.Count);
            Assert.AreEqual(eventAttrbiute.Name, this.sentEvents.ElementAt(0).Name);
            Assert.AreEqual("eventArgs", (object)this.sentEvents.ElementAt(0).EventArgs);
        }

        #endregion

        #region Event Name

        [TestMethod]
        public void EventAttributteNameIsEmpty()
        {
            var eventAttrbiute = new EventAttribute();
            var method = this.GetMethod("EventAttributteNameIsEmpty");

            eventAttrbiute.OnExit(this, method,"abc");

            Assert.AreEqual(1, this.sentEvents.Count);
            // EventAttributteNameIsEmpty  + Event
            Assert.AreEqual(this.sentEvents.ElementAt(0).Name, "EventAttributteNameIsEmptyEvent");
        }

        [TestMethod]
        public void NameRemoveOnEvent()
        {
            var eventAttrbiute = new EventAttribute();
            var method = this.GetMethod("NameRemoveOnEvent");

            eventAttrbiute.OnExit(this, method, "abc");

            Assert.AreEqual(1, this.sentEvents.Count);
            // NameRemoveOnEvent - OnEvent  + Event
            Assert.AreEqual(this.sentEvents.ElementAt(0).Name, "NameRemoveEvent");
        }

        [TestMethod]
        public void SetEventName()
        {
            var eventAttrbiute = new EventAttribute();
            eventAttrbiute.Name = "SelfSetName";

            var method = this.GetMethod("SetEventName");

            eventAttrbiute.OnExit(this, method, "abc");

            Assert.AreEqual(1, this.sentEvents.Count);
            Assert.AreEqual(this.sentEvents.ElementAt(0).Name, "SelfSetName");
        }

        #endregion

        #region helper

        public MethodBase GetMethod(string name)
        {
            return this.GetType().GetMethods().FirstOrDefault(item => item.Name.Equals(name));
        }

        #endregion
    }
}
